/**
  *             Copyright (C) SOJO Electric CO., Ltd. 2017-2018. All right reserved.
  * @file:      drv_w5500_conf.c
  * @brief:     The driver of w5500.
  * @version:   V1.0.0
  * @author:    Lexun
  * @date:      2017-02-08
  * @update:    [2017-12-07][Lexun][make the code cleanup]
  */


/* INCLUDE FILES -------------------------------------------------------------*/
#include <stddef.h>
#include "drv_w5500_conf.h"

/**
 * @brief Default function to enable interrupt.
 * @note This function help not to access wrong address. If you do not describe this function or register any functions,
 * null function is called.
 */
void 	  wizchip_cris_enter(void)           {};
/**
 * @brief Default function to disable interrupt.
 * @note This function help not to access wrong address. If you do not describe this function or register any functions,
 * null function is called.
 */
void 	  wizchip_cris_exit(void)          {};
/**
 * @brief Default function to select chip.
 * @note This function help not to access wrong address. If you do not describe this function or register any functions,
 * null function is called.
 */
void 	wizchip_cs_select(void)            {};
/**
 * @brief Default function to deselect chip.
 * @note This function help not to access wrong address. If you do not describe this function or register any functions,
 * null function is called.
 */
void 	wizchip_cs_deselect(void)          {};
/**
 * @brief Default function to read in direct or indirect interface.
 * @note This function help not to access wrong address. If you do not describe this function or register any functions,
 * null function is called.
 */
 //M20140501 : Explict pointer type casting
//uint8_t wizchip_bus_readbyte(uint32_t AddrSel) { return * ((volatile uint8_t *) AddrSel); };
uint8_t wizchip_bus_readbyte(uint32_t AddrSel) { return * ((volatile uint8_t *)((ptrdiff_t) AddrSel)); };
/**
 * @brief Default function to write in direct or indirect interface.
 * @note This function help not to access wrong address. If you do not describe this function or register any functions,
 * null function is called.
 */
 
//M20140501 : Explict pointer type casting
//void 	wizchip_bus_writebyte(uint32_t AddrSel, uint8_t wb)  { *((volatile uint8_t*) AddrSel) = wb; };
void 	wizchip_bus_writebyte(uint32_t AddrSel, uint8_t wb)  { *((volatile uint8_t*)((ptrdiff_t)AddrSel)) = wb; };

/**
 * @brief Default function to read in SPI interface.
 * @note This function help not to access wrong address. If you do not describe this function or register any functions,
 * null function is called.
 */
 #if RT_USING_W5500_2404 
uint8_t wizchip_spi_readbyte(void)        {return SPI2_ReadByte();}; 
#else
uint8_t wizchip_spi_readbyte(void)        {return 0;};  
#endif /* END RT_USING_W5500_2404 */
/**
 * @brief Default function to write in SPI interface.
 * @note This function help not to access wrong address. If you do not describe this function or register any functions,
 * null function is called.
 */
 #if RT_USING_W5500_2404 
void 	wizchip_spi_writebyte(uint8_t wb) {SPI2_WriteByte(wb);};
#else
void 	wizchip_spi_writebyte(uint8_t wb) {};
#endif /* END RT_USING_W5500_2404 */

/**
 * @\ref _WIZCHIP instance
 */
_WIZCHIP  WIZCHIP =
      {
      .id                  = _WIZCHIP_ID_,
      .if_mode             = _WIZCHIP_IO_MODE_,
      .CRIS._enter         = wizchip_cris_enter,
      .CRIS._exit          = wizchip_cris_exit,
      .CS._select          = wizchip_cs_select,
      .CS._deselect        = wizchip_cs_deselect,
//      .IF.BUS._read_byte   = wizchip_bus_readbyte,
//      .IF.BUS._write_byte  = wizchip_bus_writebyte
      .IF.SPI._read_byte   = wizchip_spi_readbyte,
      .IF.SPI._write_byte  = wizchip_spi_writebyte
      };

static uint8_t    _DNS_[4];      // DNS server ip address
//static dhcp_mode  _DHCP_;        // DHCP mode

void reg_wizchip_cris_cbfunc(void(*cris_en)(void), void(*cris_ex)(void))
{
    if (!cris_en || !cris_ex)
    {
        WIZCHIP.CRIS._enter = wizchip_cris_enter;
        WIZCHIP.CRIS._exit  = wizchip_cris_exit;
    }
    else
    {
        WIZCHIP.CRIS._enter = cris_en;
        WIZCHIP.CRIS._exit  = cris_ex;
    }
}

void reg_wizchip_cs_cbfunc(void(*cs_sel)(void), void(*cs_desel)(void))
{
    if(!cs_sel || !cs_desel)
    {
        WIZCHIP.CS._select   = wizchip_cs_select;
        WIZCHIP.CS._deselect = wizchip_cs_deselect;
    }
    else
    {
        WIZCHIP.CS._select   = cs_sel;
        WIZCHIP.CS._deselect = cs_desel;
    }
}

void reg_wizchip_bus_cbfunc(uint8_t(*bus_rb)(uint32_t addr), void (*bus_wb)(uint32_t addr, uint8_t wb))
{
    while (!(WIZCHIP.if_mode & _WIZCHIP_IO_MODE_BUS_));
   
    if(!bus_rb || !bus_wb)
    {
        WIZCHIP.IF.BUS._read_byte   = wizchip_bus_readbyte;
        WIZCHIP.IF.BUS._write_byte  = wizchip_bus_writebyte;
    }
    else
    {
        WIZCHIP.IF.BUS._read_byte   = bus_rb;
        WIZCHIP.IF.BUS._write_byte  = bus_wb;
    }
}

void reg_wizchip_spi_cbfunc(uint8_t (*spi_rb)(void), void (*spi_wb)(uint8_t wb))
{
    while (!(WIZCHIP.if_mode & _WIZCHIP_IO_MODE_SPI_));
   
    if (!spi_rb || !spi_wb)
    {
        WIZCHIP.IF.SPI._read_byte   = wizchip_spi_readbyte;
        WIZCHIP.IF.SPI._write_byte  = wizchip_spi_writebyte;
    }
    else
    {
        WIZCHIP.IF.SPI._read_byte   = spi_rb;
        WIZCHIP.IF.SPI._write_byte  = spi_wb;
    }
}

int8_t ctlwizchip(ctlwizchip_type cwtype, void* arg)
{
    uint8_t tmp = 0;
    uint8_t* ptmp[2] = {0,0};
		
    switch (cwtype)
    {
        case CW_RESET_WIZCHIP:
            wizchip_sw_reset();
            break;
				
        case CW_INIT_WIZCHIP:
            if(arg != 0) 
            {
                ptmp[0] = (uint8_t*)arg;
                ptmp[1] = ptmp[0] + _WIZCHIP_SOCK_NUM_;
            }
            return wizchip_init(ptmp[0], ptmp[1]);
						
        case CW_CLR_INTERRUPT:
            wizchip_clrinterrupt(*((intr_kind*)arg));
            break;
				
        case CW_GET_INTERRUPT:
            *((intr_kind*)arg) = wizchip_getinterrupt();
            break;
				
        case CW_SET_INTRMASK:
            wizchip_setinterruptmask(*((intr_kind*)arg));
            break; 
        
        case CW_GET_INTRMASK:
            *((intr_kind*)arg) = wizchip_getinterruptmask();
            break;
				
      #if _WIZCHIP_ > 5100
        case CW_SET_INTRTIME:
            setINTLEVEL(*(uint16_t*)arg);
            break;
				
        case CW_GET_INTRTIME:
            *(uint16_t*)arg = getINTLEVEL();
            break;
      #endif /* END _WIZCHIP_ */
				
        case CW_GET_ID:
            ((uint8_t*)arg)[0] = WIZCHIP.id[0];
            ((uint8_t*)arg)[1] = WIZCHIP.id[1];
            ((uint8_t*)arg)[2] = WIZCHIP.id[2];
            ((uint8_t*)arg)[3] = WIZCHIP.id[3];
            ((uint8_t*)arg)[4] = WIZCHIP.id[4];
            ((uint8_t*)arg)[5] = 0;
            break;
				
      #if _WIZCHIP_ ==  5500
        case CW_RESET_PHY:
            wizphy_reset();
            break;
				
        case CW_SET_PHYCONF:
            wizphy_setphyconf((wiz_PhyConf*)arg);
            break;
				
        case CW_GET_PHYCONF:
            wizphy_getphyconf((wiz_PhyConf*)arg);
            break;
				
        case CW_GET_PHYSTATUS:
            break;
				
        case CW_SET_PHYPOWMODE:
            return wizphy_setphypmode(*(uint8_t*)arg);
      #endif /* END _WIZCHIP_ */
				
        case CW_GET_PHYPOWMODE:
            tmp = wizphy_getphypmode();
            if ((int8_t)tmp == -1) 
            {
                return -1;
            }
            *(uint8_t*)arg = tmp;
            break;
				
        case CW_GET_PHYLINK:
            tmp = wizphy_getphylink();
            if ((int8_t)tmp == -1) 
            {
                return -1;
            }
            *(uint8_t*)arg = tmp;
            break;
				
        default:
            return -1;
    }
		
    return 0;
}


int8_t ctlnetwork(ctlnetwork_type cntype, void* arg)
{
    switch (cntype)
    {
        case CN_SET_NETINFO:
            wizchip_setnetinfo((wiz_NetInfo*)arg);
            break;
				
        case CN_GET_NETINFO:
            wizchip_getnetinfo((wiz_NetInfo*)arg);
            break;
				
        case CN_SET_NETMODE:
            return wizchip_setnetmode(*(netmode_type*)arg);
				
        case CN_GET_NETMODE:
            *(netmode_type*)arg = wizchip_getnetmode();
            break;
				
        case CN_SET_TIMEOUT:
            wizchip_settimeout((wiz_NetTimeout*)arg);
            break;
				
        case CN_GET_TIMEOUT:
            wizchip_gettimeout((wiz_NetTimeout*)arg);
            break;
				
        default:
            return -1;
    }
		
    return 0;
}

void wizchip_sw_reset(void)
{
    uint8_t gw[4], sn[4], sip[4];
    uint8_t mac[6];
    getSHAR(mac);
    getGAR(gw);  getSUBR(sn);  getSIPR(sip);
    setMR(MR_RST);
    getMR(); // for delay
    setSHAR(mac);
    setGAR(gw);
    setSUBR(sn);
    setSIPR(sip);
}

int8_t wizchip_init(uint8_t* txsize, uint8_t* rxsize)
{
    int8_t i;
    int8_t tmp = 0;
    wizchip_sw_reset();
    if(txsize)
    {
        tmp = 0;
        for(i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++)
        {
            tmp += txsize[i];
        }
        if (tmp > 16)
        {
            return -1;
        }
        for(i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++)
        {
            setSn_TXBUF_SIZE(i, txsize[i]);
        }
    }
		
    if (rxsize)
    {
        tmp = 0;
        for (i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++)
        {
            tmp += rxsize[i];
        }
				
        if (tmp > 16) 
        {
            return -1;
        }
        for (i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++)
        {
            setSn_RXBUF_SIZE(i, rxsize[i]);
        }
    }
		
    return 0;
}

void wizchip_clrinterrupt(intr_kind intr)
{
    uint8_t ir  = (uint8_t)intr;
    uint8_t sir = (uint8_t)((uint16_t)intr >> 8);
	
  #if _WIZCHIP_ < 5500
    ir |= (1<<4); // IK_WOL
  #endif /* END _WIZCHIP_ */
	
  #if _WIZCHIP_ == 5200
    ir |= (1 << 6);
  #endif /* END _WIZCHIP_ */
   
  #if _WIZCHIP_ < 5200
    sir &= 0x0F;
  #endif /* END _WIZCHIP_ */

  #if _WIZCHIP_ == 5100
    ir |= sir;
    setIR(ir);
  #else
    setIR(ir);
    setSIR(sir);
  #endif /* END _WIZCHIP_ */   
}

intr_kind wizchip_getinterrupt(void)
{
    uint8_t ir  = 0;
    uint8_t sir = 0;
    uint16_t ret = 0;
	
  #if _WIZCHIP_ == 5100
    ir = getIR();
    sir = ir 0x0F;
  #else
    ir  = getIR();
    sir = getSIR();
  #endif /* END _WIZCHIP_ == 5100 */        

  #if _WIZCHIP_ < 5500
    ir &= ~(1<<4); // IK_WOL
  #endif /* END _WIZCHIP_ < 5500 */     
	
  #if _WIZCHIP_ == 5200
    ir &= ~(1 << 6);
  #endif /* END _WIZCHIP_ == 5100 */     
    ret = sir;
    ret = (ret << 8) + ir;
	
    return (intr_kind)ret;
}

void wizchip_setinterruptmask(intr_kind intr)
{
    uint8_t imr  = (uint8_t)intr;
    uint8_t simr = (uint8_t)((uint16_t)intr >> 8);
  #if _WIZCHIP_ < 5500
    imr &= ~(1<<4); // IK_WOL
  #endif /* END _WIZCHIP_ < 5500 */    
	
  #if _WIZCHIP_ == 5200
    imr &= ~(1 << 6);
  #endif /* END _WIZCHIP_ == 5200 */    
   
  #if _WIZCHIP_ < 5200
    simr &= 0x0F;
  #endif /* END _WIZCHIP_ < 5200 */    

  #if _WIZCHIP_ == 5100
    imr |= simr;
    setIMR(imr);
  #else
    setIMR(imr);
    setSIMR(simr);
  #endif /* END _WIZCHIP_ == 5100 */   
} 

intr_kind wizchip_getinterruptmask(void)
{
    uint8_t imr  = 0;
    uint8_t simr = 0;
    uint16_t ret = 0;
  #if _WIZCHIP_ == 5100
    imr  = getIMR();
    simr = imr 0x0F;
  #else
    imr  = getIMR();
    simr = getSIMR();
  #endif /* END _WIZCHIP_ == 5100 */

  #if _WIZCHIP_ < 5500
    imr &= ~(1<<4); // IK_WOL
  #endif /* END _WIZCHIP_ < 5500 */
	
  #if _WIZCHIP_ == 5200
    imr &= ~(1 << 6);  // IK_DEST_UNREACH
  #endif /* END _WIZCHIP_ == 5100 */
    ret = simr;
    ret = (ret << 8) + imr;
	
    return (intr_kind)ret;
}

int8_t wizphy_getphylink(void)
{
    int8_t tmp;
  #if   _WIZCHIP_ == 5200
    if(getPHYSTATUS() & PHYSTATUS_LINK)
    {
        tmp = PHY_LINK_ON;
    }
    else
    {
        tmp = PHY_LINK_OFF;
    }
  #elif _WIZCHIP_ == 5500
    if(getPHYCFGR() & PHYCFGR_LNK_ON)
    {
        tmp = PHY_LINK_ON;
    }
    else
    {
        tmp = PHY_LINK_OFF;
    }
  #else
    tmp = -1;
  #endif
		
    return tmp;
}

#if _WIZCHIP_ > 5100

int8_t wizphy_getphypmode(void)
{
    int8_t tmp = 0;
  #if   _WIZCHIP_ == 5200
    if(getPHYSTATUS() & PHYSTATUS_POWERDOWN)
    {
        tmp = PHY_POWER_DOWN;
    }
    else    
    {      
        tmp = PHY_POWER_NORM;
    }
  #elif _WIZCHIP_ == 5500
    if(getPHYCFGR() & PHYCFGR_OPMDC_PDOWN)
    {      
        tmp = PHY_POWER_DOWN;
    }
    else 
    {      
        tmp = PHY_POWER_NORM;
    }
  #else
      tmp = -1;
  #endif
		
   return tmp;
}
#endif

#if _WIZCHIP_ == 5500
void wizphy_reset(void)
{
    uint8_t tmp = getPHYCFGR();
    tmp &= PHYCFGR_RST;
    setPHYCFGR(tmp);
    tmp = getPHYCFGR(); 
    tmp |= ~PHYCFGR_RST;
    setPHYCFGR(tmp);
}

void wizphy_setphyconf(wiz_PhyConf* phyconf)
{
    uint8_t tmp = 0;
	
    if(phyconf->by == PHY_CONFBY_SW)
    {
        tmp |= PHYCFGR_OPMD;
		}
    else
    {
        tmp &= ~PHYCFGR_OPMD;
    }
		
    if(phyconf->mode == PHY_MODE_AUTONEGO)
    {
        tmp |= PHYCFGR_OPMDC_ALLA;
    }
    else
    {
        if(phyconf->duplex == PHY_DUPLEX_FULL)
        {
            if(phyconf->speed == PHY_SPEED_100)
            {
                tmp |= PHYCFGR_OPMDC_100F;
            }
            else
            {
                tmp |= PHYCFGR_OPMDC_10F;
            }
        }   
        else
        {
            if(phyconf->speed == PHY_SPEED_100)
            {
                tmp |= PHYCFGR_OPMDC_100H;
            }
            else
            {
                tmp |= PHYCFGR_OPMDC_10H;
            }
        }
    }
    setPHYCFGR(tmp);
    wizphy_reset();
}

void wizphy_getphyconf(wiz_PhyConf* phyconf)
{
    uint8_t tmp = 0;
    tmp = getPHYCFGR();
    phyconf->by   = (tmp & PHYCFGR_OPMD) ? PHY_CONFBY_SW : PHY_CONFBY_HW;
	
    switch (tmp & PHYCFGR_OPMDC_ALLA)
    {
        case PHYCFGR_OPMDC_ALLA:
        case PHYCFGR_OPMDC_100FA: 
            phyconf->mode = PHY_MODE_AUTONEGO;
            break;
        default:
            phyconf->mode = PHY_MODE_MANUAL;
            break;
    }
		
    switch (tmp & PHYCFGR_OPMDC_ALLA)
    {
        case PHYCFGR_OPMDC_100FA:
        case PHYCFGR_OPMDC_100F:
        case PHYCFGR_OPMDC_100H:
            phyconf->speed = PHY_SPEED_100;
            break;
				
        default:
            phyconf->speed = PHY_SPEED_10;
            break;
    }
		
    switch (tmp & PHYCFGR_OPMDC_ALLA)
    {
        case PHYCFGR_OPMDC_100FA:
        case PHYCFGR_OPMDC_100F:
        case PHYCFGR_OPMDC_10F:
            phyconf->duplex = PHY_DUPLEX_FULL;
            break;
				
        default:
            phyconf->duplex = PHY_DUPLEX_HALF;
            break;
    }
}

void wizphy_getphystat(wiz_PhyConf* phyconf)
{
    uint8_t tmp = getPHYCFGR();
    phyconf->duplex = (tmp & PHYCFGR_DPX_FULL) ? PHY_DUPLEX_FULL : PHY_DUPLEX_HALF;
    phyconf->speed  = (tmp & PHYCFGR_SPD_100) ? PHY_SPEED_100 : PHY_SPEED_10;
}

int8_t wizphy_setphypmode(uint8_t pmode)
{
    uint8_t tmp = 0;
	
    tmp = getPHYCFGR();
    if ((tmp & PHYCFGR_OPMD)== 0) 
    {
        return -1;
    }
    tmp &= ~PHYCFGR_OPMDC_ALLA;         
	
    if( pmode == PHY_POWER_DOWN)
		{
        tmp |= PHYCFGR_OPMDC_PDOWN;
		}
		else
		{
        tmp |= PHYCFGR_OPMDC_ALLA;
		}
		setPHYCFGR(tmp);
		wizphy_reset();
		tmp = getPHYCFGR();
		if (pmode == PHY_POWER_DOWN)
		{
				if (tmp & PHYCFGR_OPMDC_PDOWN) 
				{
						return 0;
				}
		}
		else
		{
				if (tmp & PHYCFGR_OPMDC_ALLA) 
				{
				    return 0;
				}
		}
		
		return -1;
}
#endif


void wizchip_setnetinfo(wiz_NetInfo* pnetinfo)
{
    setSHAR(pnetinfo->mac);
    setGAR(pnetinfo->gw);
    setSUBR(pnetinfo->sn);
    setSIPR(pnetinfo->ip);
    _DNS_[0] = pnetinfo->dns[0];
    _DNS_[1] = pnetinfo->dns[1];
    _DNS_[2] = pnetinfo->dns[2];
    _DNS_[3] = pnetinfo->dns[3];
    //_DHCP_   = pnetinfo->dhcp;
}

void wizchip_getnetinfo(wiz_NetInfo* pnetinfo)
{
    getSHAR(pnetinfo->mac);
    getGAR(pnetinfo->gw);
    getSUBR(pnetinfo->sn);
    getSIPR(pnetinfo->ip);
    pnetinfo->dns[0]= _DNS_[0];
    pnetinfo->dns[1]= _DNS_[1];
    pnetinfo->dns[2]= _DNS_[2];
    pnetinfo->dns[3]= _DNS_[3];
    //pnetinfo->dhcp  = _DHCP_;
}

int8_t wizchip_setnetmode(netmode_type netmode)
{
    uint8_t tmp = 0;
  #if _WIZCHIP_ != 5500   
    if (netmode & ~(NM_WAKEONLAN | NM_PPPOE | NM_PINGBLOCK)) 
    {
        return -1;
    }
  #else
    if (netmode & ~(NM_WAKEONLAN | NM_PPPOE | NM_PINGBLOCK | NM_FORCEARP)) 
    {
        return -1;
    }
  #endif /* END _WIZCHIP_ != 5500 */
		
    tmp = getMR();
    tmp |= (uint8_t)netmode;
    setMR(tmp);
		
    return 0;
}

netmode_type wizchip_getnetmode(void)
{
    return (netmode_type) getMR();
}

void wizchip_settimeout(wiz_NetTimeout* nettime)
{
    setRCR(nettime->retry_cnt);
    setRTR(nettime->time_100us);
}

void wizchip_gettimeout(wiz_NetTimeout* nettime)
{
    nettime->retry_cnt = getRCR();
    nettime->time_100us = getRTR();
}

/* END OF FILE ---------------------------------------------------------------*/

